package com.nulldev.util.other.arrays;

import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Queue;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.SynchronousQueue;

import com.nulldev.util.FinalBypass;
import com.nulldev.util.java.JVMUtil;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.threading.legacy.ThreadExecutor;
import com.nulldev.util.threading.v3.RunnableWorker;
import com.nulldev.util.threading.v3.ThreadDispatcher;

/**
 * 
 * A queued list preventing Exceptions caused from reading lists too quickly. <br>
 * <b> WARNING: </b> Calling any function except close(), size(), addContext() will block the current thread!
 * 
 * @author null
 * @since nullUtil 2.6 - build 204
 * @param <T>
 */
public class QueuedList<T> implements List<T>, Runnable, Closeable, Cloneable {

	private final List<T> list;
	private final ArrayList<QueueContext<List<T>>> queue = 
			new ArrayList<QueueContext<List<T>>>();
	private static final Logger log = LoggerFactory.getLogger(true, true);
	
	public QueuedList(final List<T> list) {
		this.list = list;
		ThreadDispatcher.instance().addTask(RunnableWorker.fromRunnable(this, "$QueuedList.Run"));
	}

	private boolean __is_running, stop;
	
	public void start() {
		if (this.__is_running) {
			return;
		}
		ThreadDispatcher.instance().addTask(RunnableWorker.fromRunnable(this, "$QueuedList.Run"));
	}
	
	@Override
	public void run() {
		if (this.__is_running) {
			return;
		}
		this.__is_running = true;
		while (!this.stop) {
			try {
				if (this.list == null) {
					log.error("Queue STOPPED: List is null!");
					break;
				}
				if (!this.queue.isEmpty()) {
					final QueueContext<List<T>> context = this.queue.remove(0);
					if (context == null) {
						//JVMUtil.async_println("NO CONTEXT FOUND!");
						try {
							Thread.sleep(5L);
						}catch (Exception e) {}
					}else {
						//JVMUtil.async_println("Context ready!");
						context.onContext(this.list);
					}
				}
			}catch (Exception e) {
				log.error("Failed a context task!", e);
			}
		}
		this.__is_running = false;
		this.stop = false;
	}
	
	

	@Override
	public void close() throws IOException {
		this.stop = true;
	}

	public boolean addContext(final QueueContext<List<T>> context) {
		return this.queue.add(context);
	}


	@Override
	public boolean add(final T e) {
		return addContext(new QueueContext<List<T>>() {

			@Override
			public void onContext(List<T> item) {
				final boolean result = item.add(e);
				JVMUtil.async_print(""); //FIXME: Nice JVM, Oracle. (broken JIT compiler)
				//log.trace("add(T): result: " + result);
			}
		});
	}



	@Override
	public void add(int index, T element) {
		addContext(new QueueContext<List<T>>() {

			@Override
			public void onContext(List<T> item) {
				item.add(index, element);
			}
		});
	}



	@Override
	public boolean addAll(final Collection<? extends T> c) {
		return addContext(new QueueContext<List<T>>() {

			@Override
			public void onContext(List<T> item) {
				item.addAll(c);
			}
		});
	}



	@Override
	public boolean addAll(int index, Collection<? extends T> c) {
		return addContext(new QueueContext<List<T>>() {

			@Override
			public void onContext(List<T> item) {
				item.addAll(index, c);
			}
		});
	}



	@Override
	public void clear() {
		addContext(new QueueContext<List<T>>() {

			@Override
			public void onContext(List<T> item) {
				item.clear();
			}
		});
	}



	@Override
	public boolean contains(Object o) {
		final FinalBypass<Boolean> result = FinalBypass.createBypass(Boolean.class);
		final FinalBypass<Boolean> turn = FinalBypass.createBypass(Boolean.class);
		addContext(new QueueContext<List<T>>() {

			@Override
			public void onContext(List<T> item) {
				result.setObject(item.contains(o));
				turn.setObject(true);
			}
		});
		while (!turn.getObject());
		return result.getObject();
	}



	@Override
	public boolean containsAll(Collection<?> c) {
		final FinalBypass<Boolean> result = FinalBypass.createBypass(Boolean.class);
		final FinalBypass<Boolean> turn = FinalBypass.createBypass(Boolean.class);
		addContext(new QueueContext<List<T>>() {

			@Override
			public void onContext(List<T> item) {
				result.setObject(item.containsAll(c));
				turn.setObject(true);
			}
		});
		while (!turn.getObject());
		return result.getObject();
	}



	@Override
	public T get(int index) {
		final FinalBypass<T> result = new FinalBypass<T>(null);
		final FinalBypass<Boolean> turn = FinalBypass.createBypass(Boolean.class);
		addContext(new QueueContext<List<T>>() {

			@Override
			public void onContext(List<T> item) {
				final T res = item.get(index);
				result.setObject(res);
				turn.setObject(true);
			}
		});
		while (!turn.getObject());
		return result.getObject();
	}

	@Override
	public int indexOf(Object o) {
		final FinalBypass<Integer> result = new FinalBypass<Integer>(0);
		final FinalBypass<Boolean> turn = FinalBypass.createBypass(Boolean.class);
		addContext(new QueueContext<List<T>>() {

			@Override
			public void onContext(List<T> item) {
				result.setObject(item.indexOf(o));
				turn.setObject(true);
			}
		});
		while (!turn.getObject());
		return result.getObject();
	}



	@Override
	public boolean isEmpty() {
		return this.list.isEmpty();
	}



	@Override
	public Iterator<T> iterator() {
		final FinalBypass<Iterator<T>> result = new FinalBypass<Iterator<T>>(null);
		final FinalBypass<Boolean> turn = FinalBypass.createBypass(Boolean.class);
		addContext(new QueueContext<List<T>>() {

			@Override
			public void onContext(List<T> item) {
				result.setObject(item.iterator());
				turn.setObject(true);
			}
		});
		while (!turn.getObject());
		return result.getObject();
	}



	@Override
	public int lastIndexOf(Object o) {
		final FinalBypass<Integer> result = new FinalBypass<Integer>(0);
		final FinalBypass<Boolean> turn = FinalBypass.createBypass(Boolean.class);
		addContext(new QueueContext<List<T>>() {

			@Override
			public void onContext(List<T> item) {
				result.setObject(item.lastIndexOf(o));
				turn.setObject(true);
			}
		});
		while (!turn.getObject());
		return result.getObject();
	}



	@Override
	public ListIterator<T> listIterator() {
		final FinalBypass<ListIterator<T>> result = new FinalBypass<ListIterator<T>>(null);
		final FinalBypass<Boolean> turn = FinalBypass.createBypass(Boolean.class);
		addContext(new QueueContext<List<T>>() {

			@Override
			public void onContext(List<T> item) {
				result.setObject(item.listIterator());
				turn.setObject(true);
			}
		});
		while (!turn.getObject());
		return result.getObject();
	}



	@Override
	public ListIterator<T> listIterator(int index) {
		final FinalBypass<ListIterator<T>> result = new FinalBypass<ListIterator<T>>(null);
		final FinalBypass<Boolean> turn = FinalBypass.createBypass(Boolean.class);
		addContext(new QueueContext<List<T>>() {

			@Override
			public void onContext(List<T> item) {
				result.setObject(item.listIterator(index));
				turn.setObject(true);
			}
		});
		while (!turn.getObject());
		return result.getObject();
	}



	@Override
	public boolean remove(Object o) {
		final FinalBypass<Boolean> result = new FinalBypass<Boolean>(false);
		final FinalBypass<Boolean> turn = FinalBypass.createBypass(Boolean.class);
		addContext(new QueueContext<List<T>>() {

			@Override
			public void onContext(List<T> item) {
				result.setObject(item.remove(o));
				turn.setObject(true);
			}
		});
		while (!turn.getObject());
		return result.getObject();
	}



	@Override
	public T remove(int index) {
		final FinalBypass<T> result = new FinalBypass<T>(null);
		final FinalBypass<Boolean> turn = FinalBypass.createBypass(Boolean.class);
		addContext(new QueueContext<List<T>>() {

			@Override
			public void onContext(List<T> item) {
				result.setObject(item.remove(index));
				turn.setObject(true);
			}
		});
		while (!turn.getObject());
		return result.getObject();
	}



	@Override
	public boolean removeAll(Collection<?> c) {
		final FinalBypass<Boolean> result = new FinalBypass<Boolean>(false);
		final FinalBypass<Boolean> turn = FinalBypass.createBypass(Boolean.class);
		addContext(new QueueContext<List<T>>() {

			@Override
			public void onContext(List<T> item) {
				result.setObject(item.removeAll(c));
				turn.setObject(true);
			}
		});
		while (!turn.getObject());
		return result.getObject();
	}



	@Override
	public boolean retainAll(Collection<?> c) {
		final FinalBypass<Boolean> result = new FinalBypass<Boolean>(false);
		final FinalBypass<Boolean> turn = FinalBypass.createBypass(Boolean.class);
		addContext(new QueueContext<List<T>>() {

			@Override
			public void onContext(List<T> item) {
				result.setObject(item.retainAll(c));
				turn.setObject(true);
			}
		});
		while (!turn.getObject());
		return result.getObject();
	}



	@Override
	public T set(int index, T element) {
		final FinalBypass<T> result = new FinalBypass<T>(element);
		final FinalBypass<Boolean> turn = FinalBypass.createBypass(Boolean.class);
		addContext(new QueueContext<List<T>>() {

			@Override
			public void onContext(List<T> item) {
				result.setObject(item.set(index, element));
				turn.setObject(true);
			}
		});
		while (!turn.getObject());
		return result.getObject();
	}



	@Override
	public int size() {
		return this.list.size();
	}



	@Override
	public List<T> subList(int fromIndex, int toIndex) {
		final FinalBypass<List<T>> result = new FinalBypass<List<T>>(null);
		final FinalBypass<Boolean> turn = FinalBypass.createBypass(Boolean.class);
		addContext(new QueueContext<List<T>>() {

			@Override
			public void onContext(List<T> item) {
				result.setObject(item.subList(fromIndex, toIndex));
				turn.setObject(true);
			}
		});
		while (!turn.getObject());
		return result.getObject();
	}



	@Override
	public Object[] toArray() {
		return this.list.toArray();
	}



	@Override
	public <T> T[] toArray(T[] a) {
		return this.list.toArray(a);
	}
	
	public static void main(String[] args) {
		final QueuedList<String> test = new QueuedList<String>(new LinkedList<String>());
		long start = System.currentTimeMillis();
		test.add("Hello world!");
		long end = System.currentTimeMillis();
		JVMUtil.async_println("Time taken to add 'Hello world!': " + (end - start) + " ms");
		JVMUtil.async_println("Trying to read value...");
		start = System.currentTimeMillis();
		final String r = test.get(0);
		end = System.currentTimeMillis();
		JVMUtil.async_println("Time taken to get 'Hello world!': " + (end - start) + " ms");
		JVMUtil.async_println("Reading index 0 x100...");
		start = System.currentTimeMillis();
		for (int i = 0; i < 100; i++) {
			JVMUtil.async_println("Reading index 0 x100..." + (i + 1));
			JVMUtil.async_println(test.get(0));
		}
		end = System.currentTimeMillis();
		JVMUtil.async_println("Time taken to get 'Hello world!' x100: " + (end - start) + " ms");
		try {
			test.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public int queueSize() {
		return this.queue.size();
	}
	
	@Override
	public QueuedList<T> clone() {
		final QueuedList<T> list = new QueuedList<>(this.list);
		return list;
	}
}
